//////////////////////////////////////////////////////
/////// SQUARE ASSAULT OFFICIAL LEVEL EDITOR /////////
/////// BY JORDAN BROWN, AKA DR.MELON        /////////
//////////////////////////////////////////////////////

// WE USE THE DORYEN LIBRARY HERE AS WELL AS IN SQUARE ASSAULT.
#include "libtcod.hpp"
#include <iostream>
#include <string>
#include <fstream.h>
#include <sstream>
#include <cstring>

#define MAP_HEIGHT 30
#define MAP_WIDTH 80

// Tile Types
#define TILE_FLOOR	            0
#define TILE_WALL		        1
#define TILE_LAVA_LIGHT	        2
#define TILE_SMOKE		        3
#define TILE_WALL_DARK	        4
#define TILE_SWITCH_WALLS		5
#define SAFE_PLACE		        6
#define TILE_LAVA_DARK          7
#define TILE_SWITCH_LAVA        8
#define TILE_START              9
#define TILE_GOAL               10

using namespace std;

void fDrawEditorFrames();
void fDrawTileSelection();
void fSave(int map[MAP_HEIGHT][MAP_WIDTH]);
void fLoad(int map[MAP_HEIGHT][MAP_WIDTH]);
void fWriteOnMap();
void fDrawButtons();
void fMouseCheck();
void fLoadBox();
void fSaveBox();
void fNameBox();
void DrawMap( void);
void DrawTile( int x, int y);
char *append_char ( const char *s, const char c );


//Dialog Boxes

TCODConsole *LoadDialog = new TCODConsole(18,6);
TCODConsole *SaveDialog = new TCODConsole(18,6);
TCODConsole *NameDialog = new TCODConsole(18,6);

// The map
int nMapArray[ MAP_HEIGHT ][ MAP_WIDTH ];

bool bDone = false;
bool bDarkLevel = false;

char * sLevelName;

//The mouse
TCOD_mouse_t mMouse;

char * LLevelFilename = "map.txt"; //Filename of the level to load.
char * SLevelFilename = "map.txt"; //Filename of the level to save.
char * sDarkOnOff = " Off.";        //Dark Mode on or off?


//Tile type declaration - simpler than in the game.
struct TILE_TYPE
{
	int 	    nCharacter;			// ASCII character for this tile type
    TCODColor   nTileColor;		//Doryenabble Color Code
};

int tSelectedTileType; //Tile to place

 TILE_TYPE  sTileIndex[] = {
	 { 219, TCODColor::black},			// (0) TILE_FLOOR
	 { 219, TCODColor::darkOrange},			// (1) TILE_WALL
	 { 219, TCODColor::red },		// (2) TILE_LAVA_LIGHT
	 { 177, TCODColor::lightGrey },			// (3) TILE_SMOKE
	 { 219, TCODColor::darkerOrange },			// (4) TILE_WALL_DARK
	 { 84, TCODColor::desaturatedGreen },			// (5) TILE_SWITCH_WALLS   //MAKE A "SAFE" BOOLEAN FOR TILES SUCH AS 5 AND 6 AND FROZEN LAVA
	 { 219, TCODColor::desaturatedBlue },			// (6) SAFE_PLACE
	 { 219, TCODColor::darkerRed },                  // (7) TILE_LAVA_DARK
	 { 84, TCODColor::darkMagenta },                //  (8) TILE_SWITCH_LAVA
	 { 177, TCODColor::cyan },                    //  (9) TILE_START
	 { 219, TCODColor::orange }                      // (10) TILE_GOAL
};

int main()
{

      TCODConsole::initRoot(80,51,"Square Assault Level Editor 1.0",false);
      TCODSystem::setFps(30); // limit framerate to 30 frames per second
      while (bDone != true && ! TCODConsole::isWindowClosed() )
        {
            // ... draw on TCODConsole::root
            fDrawEditorFrames();
            fDrawTileSelection();
            fDrawButtons();
            DrawMap();

            //..... Get input of mouse.
            mMouse = TCODMouse::getStatus();
            fMouseCheck();
            fWriteOnMap();
            TCODConsole::root->setForegroundColor(sTileIndex[tSelectedTileType].nTileColor);
            TCODConsole::root->putChar(mMouse.cx,mMouse.cy, sTileIndex[tSelectedTileType].nCharacter, TCOD_BKGND_NONE) ; //Draw the selected tile under the mouse cursor.
            TCODConsole::flush();
            TCOD_key_t key=TCODConsole::checkForKeypress();


        }

    return 0;
}


void fDrawEditorFrames()
{
    //Set colours for printing.
    TCODConsole::root->setBackgroundColor(TCODColor::black);
    TCODConsole::root->setForegroundColor(TCODColor::white);
    //Map area.
    TCODConsole::root->hline(0,30, 80, TCOD_BKGND_SET);


    TCODConsole::root->setForegroundColor(TCODColor::lightSea);
    TCODConsole::root->rect(60,31,20,20,TCOD_BKGND_SET);
    TCODConsole::root->printFrame(60,31,20,20,false,"Tiles");
    //Extras Area (click to change name, save, load etc)
    TCODConsole::root->setBackgroundColor(TCODColor::orange);
    TCODConsole::root->setForegroundColor(TCODColor::white);
    TCODConsole::root->rect(0,31,60,20,TCOD_BKGND_SET);
    TCODConsole::root->printFrame(0,31,60,20,false,"Map Settings");
    TCODConsole::root->setBackgroundColor(TCODColor::black);

}

void fDrawTileSelection()
{
    //Here we will draw the tiles the user can choose.
    for (int nType=0; nType<11; nType++)
    {
    TCODConsole::root->setForegroundColor(sTileIndex[nType].nTileColor); //Doryen Tile Coloring
    TCODConsole::root->putChar(62, (33 + nType), sTileIndex[nType].nCharacter, TCOD_BKGND_NONE); //Place a row of tiles downwards.
    }
    //Print off list of what the tiles are.
    TCODConsole::root->printLeft(64, 33, TCOD_BKGND_NONE, "= Floor");
    TCODConsole::root->printLeft(64, 34, TCOD_BKGND_NONE, "= Wall");
    TCODConsole::root->printLeft(64, 35, TCOD_BKGND_NONE, "= Lava");
    TCODConsole::root->printLeft(64, 36, TCOD_BKGND_NONE, "= Smoke");
    TCODConsole::root->printLeft(64, 37, TCOD_BKGND_NONE, "= Dark Wall");
    TCODConsole::root->printLeft(64, 38, TCOD_BKGND_NONE, "= Switch");
    TCODConsole::root->printLeft(64, 39, TCOD_BKGND_NONE, "= Safe Tile");
    TCODConsole::root->printLeft(64, 40, TCOD_BKGND_NONE, "= Dark Lava");
    TCODConsole::root->printLeft(64, 41, TCOD_BKGND_NONE, "= Lava Switch");
    TCODConsole::root->printLeft(64, 42, TCOD_BKGND_NONE, "= Start Tile");
    TCODConsole::root->printLeft(64, 43, TCOD_BKGND_NONE, "= Goal Tile");


}

void fDrawButtons()
{
    //We want to draw the "buttons" to choose what to do
    TCODConsole::root->setBackgroundColor(TCODColor::orange);
    TCODConsole::root->setForegroundColor(TCODColor::yellow);
    TCODConsole::root->printLeft(2, 33, TCOD_BKGND_SET, "[SAVE]");
    TCODConsole::root->printLeft(2, 34, TCOD_BKGND_SET, "[LOAD]");
    TCODConsole::root->printLeft(2, 36, TCOD_BKGND_SET, "[QUIT]");
    TCODConsole::root->printLeft(2, 37, TCOD_BKGND_SET, "[SET MAP NAME]");
    TCODConsole::root->printLeft(2, 38, TCOD_BKGND_SET, "[DARK LEVEL]");

    TCODConsole::root->printLeft(2, 39, TCOD_BKGND_SET, "File:");
    TCODConsole::root->printLeft(8, 39, TCOD_BKGND_SET, LLevelFilename);
    TCODConsole::root->printLeft(2, 40, TCOD_BKGND_SET, "Dark is:");
    TCODConsole::root->printLeft(9, 40, TCOD_BKGND_SET, sDarkOnOff);


}

void DrawTile( int x, int y )
{

	// Record the tile type
	int nType = nMapArray[y][x];
	TCODConsole::root->setForegroundColor(sTileIndex[nType].nTileColor); //Doryen Tile Coloring
	// Draw the tile to the screen
	TCODConsole::root->putChar(x, y, sTileIndex[nType].nCharacter, TCOD_BKGND_NONE) ; //Doryen Tile Printing, at X and Y.

}
//Draw the map.
void DrawMap( void )
{
	for( int y = 0; y < MAP_HEIGHT; y++ )
	{
		for( int x = 0; x < MAP_WIDTH; x++ )
		{
			DrawTile(x, y);
		}
	}
}

void fWriteOnMap()
{
            if (mMouse.cx < 80 && mMouse.cy < 30 && mMouse.lbutton == true)
            {
                nMapArray[mMouse.cy][mMouse.cx] = tSelectedTileType;
            }

}

void fMouseCheck()
{
    //Buttons Area ----------------------------------------
    //Test Button ------------------------------------------
        if (mMouse.cx <14 && mMouse.cx >1 && mMouse.cy == 37 && mMouse.lbutton == true)
            {
                TCODConsole::root->printLeft(2, 38, TCOD_BKGND_SET, "*click*");
            }
    //--------------------------------------------------
    //Load Button -------------------------------------
        if (mMouse.cx < 8 && mMouse.cx > 1 && mMouse.cy == 34 && mMouse.lbutton_pressed == true)
        {
            fLoadBox(); //Dialog box.
            fLoad(nMapArray); //Load the map.
        }
    // -------------------------------------------------
    //Save Button -------------------------------------
        if (mMouse.cx < 8 && mMouse.cx > 1 && mMouse.cy == 33 && mMouse.lbutton_pressed == true)
        {
            fSaveBox(); //Dialog box.
            fSave(nMapArray);
        }

    // -----------------------------------------------------
    //Level Name Button ----------------------------
        if (mMouse.cx < 16 && mMouse.cx > 1 && mMouse.cy == 37 && mMouse.lbutton_pressed == true)
        {
            fNameBox(); //Name dialog box.
        }

    // ------------------------------------------------
    //Dark Mode Button ------------------------------
        if (mMouse.cx < 14 && mMouse.cx > 1 && mMouse.cy == 38 && mMouse.lbutton_pressed == true)
        {
            if (bDarkLevel == false)
            {
                bDarkLevel = true;
                sDarkOnOff = " On.";
            }
            else
            {
                bDarkLevel = false;
                sDarkOnOff = " Off.";
            }
        }

    //-----------------------------------------------------
    //Quit Button ------------------------------------
        if (mMouse.cx < 8 && mMouse.cx > 1 && mMouse.cy == 36 && mMouse.lbutton_pressed == true)
        {
            bDone = true;
        }


    //Tiles Area -----------------------------------
        if (mMouse.cx == 62 && mMouse.cy == 33 && mMouse.lbutton == true)
            {
                tSelectedTileType = 0; //Choose floor
            }
        if (mMouse.cx == 62 && mMouse.cy == 34 && mMouse.lbutton == true)
            {
                tSelectedTileType = 1; //Choose wall.
            }
        if (mMouse.cx == 62 && mMouse.cy == 35 && mMouse.lbutton == true)
            {
                tSelectedTileType = 2; //Choose lava
            }
        if (mMouse.cx == 62 && mMouse.cy == 36 && mMouse.lbutton == true)
            {
                tSelectedTileType = 3; //Choose smoke.
            }
        if (mMouse.cx == 62 && mMouse.cy == 37 && mMouse.lbutton == true)
            {
                tSelectedTileType = 4; //Choose dark wall
            }
        if (mMouse.cx == 62 && mMouse.cy == 38 && mMouse.lbutton == true)
            {
                tSelectedTileType = 5; //Choose switch
            }
        if (mMouse.cx == 62 && mMouse.cy == 39 && mMouse.lbutton == true)
            {
                tSelectedTileType = 6; //Choose safe tile
            }
        if (mMouse.cx == 62 && mMouse.cy == 40 && mMouse.lbutton == true)
            {
                tSelectedTileType = 7; //Choose dark lava
            }
        if (mMouse.cx == 62 && mMouse.cy == 41 && mMouse.lbutton == true)
            {
                tSelectedTileType = 8; //Choose lava switch
            }
        if (mMouse.cx == 62 && mMouse.cy == 42 && mMouse.lbutton == true)
            {
                tSelectedTileType = 9; //Choose start tile
            }
        if (mMouse.cx == 62 && mMouse.cy == 43 && mMouse.lbutton == true)
            {
                tSelectedTileType = 10; //Choose goal tile
            }
    //----------------------------------------------

}

void fLoad(int map[MAP_HEIGHT][MAP_WIDTH])
{
    int x;
    int y;
    string s;
    string s2;
    int isdark;

    ifstream sMapFile(LLevelFilename);
    if (sMapFile.is_open())
    {


        for (y=0; y<MAP_HEIGHT; y++)
        {

            for (x=0; x<MAP_WIDTH; x++)
            {
                sMapFile >> map[y][x];
            }

        }

    }
    sMapFile.close();

    sMapFile.open(LLevelFilename);
    sMapFile.seekg (0, ios::beg); // Seek to the First Line.

    for (int i=0; i<=(MAP_HEIGHT); i++) // Loop until we hit the line with the name on.
        getline(sMapFile, s);

    sMapFile.close();

    sMapFile.open(LLevelFilename);
    sMapFile.seekg (0, ios::beg); // Go to the first line

    for (int i=0; i<=(MAP_HEIGHT + 1); i++) // loop to desired line
        getline(sMapFile, s2);
    sMapFile.close();

    stringstream ss(s2);
    ss >> isdark;

    if (isdark == 1)
    {
        bDarkLevel = true;
        sDarkOnOff = " On.";
    }
    else
    {
        bDarkLevel = false;
        sDarkOnOff = " Off.";
    }




}
void fSave(int map[MAP_HEIGHT][MAP_WIDTH])
{
    int x;
    int y;
    long pos;

    ofstream sMapFile;
    sMapFile.open(SLevelFilename);
    if (sMapFile.is_open())
    {

        for (y=0; y<MAP_HEIGHT; y++)
            {
                for (x=0; x<MAP_WIDTH; x++)
                    {
                        sMapFile << map[y][x] << " "; //Write data to file, adding a space after every entry
                    }

                sMapFile << std::endl;
            }


    }
    else
    {
        std::cout << "COULD NOT SAVE";
    }


    sMapFile.seekp (0, ios::end); // Seek to the end
    sMapFile << sLevelName;
    sMapFile << endl;



    sMapFile.seekp (0, ios::end); // Seek to the end
    if (bDarkLevel == true)
    {
        sMapFile << 1;
    }
    else
    {
        sMapFile << 0;
    }
    sMapFile.close();

}

void fLoadBox() //Load dialog box.
{
    LoadDialog->setBackgroundColor(TCODColor::darkGrey);
    LoadDialog->setForegroundColor(TCODColor::lightGreen);
    LoadDialog->printFrame(0,0,18,6,true,"Type Name");
    TCOD_key_t key;
    int xprint = 2;
    int yprint = 3;
    key.c = NULL;
    LLevelFilename = ""; //Reset Name
    while (key.vk != TCODK_ENTER)
    {
        LoadDialog->putChar(xprint,yprint,key.c, TCOD_BKGND_NONE);
        LLevelFilename = append_char(LLevelFilename,key.c);
        xprint++;
        TCODConsole::blit(LoadDialog,0,0,18,6,TCODConsole::root,30,15,255);
        TCODConsole::flush();
        key = TCODConsole::waitForKeypress(true);
    }

    TCODConsole::root->setBackgroundColor(TCODColor::black);
    TCODConsole::root->clear();
}
void fSaveBox() //Save dialog box.
{
    SaveDialog->setBackgroundColor(TCODColor::darkGrey);
    SaveDialog->setForegroundColor(TCODColor::red);
    SaveDialog->printFrame(0,0,18,6,true,"Type Name");
    TCOD_key_t key;
    int xprint = 2;
    int yprint = 3;
    key.c = NULL;
    SLevelFilename = ""; //Reset Name
    while (key.vk != TCODK_ENTER)
    {
        SaveDialog->putChar(xprint,yprint,key.c, TCOD_BKGND_NONE);
        SLevelFilename = append_char(SLevelFilename,key.c);
        xprint++;
        TCODConsole::blit(SaveDialog,0,0,18,6,TCODConsole::root,30,15,255);
        TCODConsole::flush();
        key = TCODConsole::waitForKeypress(true);
    }
    std::cout << SLevelFilename;
    TCODConsole::root->setBackgroundColor(TCODColor::black);
    TCODConsole::root->clear();
}

void fNameBox() //Level Name dialog box.
{
    NameDialog->setBackgroundColor(TCODColor::darkBlue);
    NameDialog->setForegroundColor(TCODColor::cyan);
    NameDialog->printFrame(0,0,18,6,true,"Type Name");
    TCOD_key_t key;
    int xprint = 2;
    int yprint = 3;
    key.c = NULL;
    sLevelName = ""; //Reset Name
    while (key.vk != TCODK_ENTER)
    {
        NameDialog->putChar(xprint,yprint,key.c, TCOD_BKGND_NONE);
        sLevelName = append_char(sLevelName,key.c);
        xprint++;
        TCODConsole::blit(NameDialog,0,0,18,6,TCODConsole::root,30,15,255);
        TCODConsole::flush();
        key = TCODConsole::waitForKeypress(true);
    }
    std::cout << sLevelName;
    TCODConsole::root->setBackgroundColor(TCODColor::black);
    TCODConsole::root->clear();
}

// FUNCTION FOR CONCATENATING CHAR*'s WITH CHARS.
char *append_char ( const char *s, const char c )
{
  std::size_t len = std::strlen ( s );
  char *ret = new char[len + 2];

  std::strcpy ( ret, s );
  ret[len] = c;
  ret[len + 1] = '\0';

  return ret;
}
